<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN" lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>

<title>创建及定制 Rails 生成器和模板 — Ruby on Rails Guides</title>
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print" />

<link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shCore.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shThemeRailsGuides.css" />

<link rel="stylesheet" type="text/css" href="stylesheets/fixes.css" />

<link href="images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
</head>
<body class="guide">
  <div id="topNav">
    <div class="wrapper">
      <strong class="more-info-label">更多内容 <a href="http://rubyonrails.org/">rubyonrails.org:</a> </strong>
      <span class="red-button more-info-button">
        更多内容
      </span>
      <ul class="more-info-links s-hidden">
        <li class="more-info"><a href="http://weblog.rubyonrails.org/">博客</a></li>
        <li class="more-info"><a href="http://guides.rubyonrails.org/">指南</a></li>
        <li class="more-info"><a href="http://api.rubyonrails.org/">API</a></li>
        <li class="more-info"><a href="http://stackoverflow.com/questions/tagged/ruby-on-rails">提问</a></li>
        <li class="more-info"><a href="https://github.com/rails/rails">到 GitHub 贡献</a></li>
        <li class="more-info"><a href="https://ruby-china.org/">Ruby China 社区</a></li>
      </ul>
    </div>
  </div>
  <div id="header">
    <div class="wrapper clearfix">
      <h1><a href="index.html" title="返回首页">Rails 指南</a></h1>
      <ul class="nav">
        <li><a class="nav-item" href="index.html">首页</a></li>
        <li class="guides-index guides-index-large">
          <a href="index.html" id="guidesMenu" class="guides-index-item nav-item">指南索引</a>
          <div id="guides" class="clearfix" style="display: none;">
            <hr />
              <dl class="L">
                <dt>新手入门</dt>
                <dd><a href="getting_started.html">Rails 入门</a></dd>
                <dt>模型</dt>
                <dd><a href="active_record_basics.html">Active Record 基础</a></dd>
                <dd><a href="active_record_migrations.html">Active Record 迁移</a></dd>
                <dd><a href="active_record_validations.html">Active Record 数据验证</a></dd>
                <dd><a href="active_record_callbacks.html">Active Record 回调</a></dd>
                <dd><a href="association_basics.html">Active Record 关联</a></dd>
                <dd><a href="active_record_querying.html">Active Record 查询接口</a></dd>
                <dt>视图</dt>
                <dd><a href="layouts_and_rendering.html">Rails 布局和视图渲染</a></dd>
                <dd><a href="form_helpers.html">Action View 表单辅助方法</a></dd>
                <dt>控制器</dt>
                <dd><a href="action_controller_overview.html">Action Controller 概览</a></dd>
                <dd><a href="routing.html">Rails 路由全解</a></dd>
              </dl>
              <dl class="R">
                <dt>深入探索</dt>
                <dd><a href="active_support_core_extensions.html">Active Support 核心扩展</a></dd>
                <dd><a href="i18n.html">Rails 国际化 API</a></dd>
                <dd><a href="action_mailer_basics.html">Action Mailer 基础</a></dd>
                <dd><a href="active_job_basics.html">Active Job 基础</a></dd>
                <dd><a href="testing.html">Rails 应用测试指南</a></dd>
                <dd><a href="security.html">Ruby on Rails 安全指南</a></dd>
                <dd><a href="debugging_rails_applications.html">调试 Rails 应用</a></dd>
                <dd><a href="configuring.html">配置 Rails 应用</a></dd>
                <dd><a href="command_line.html">Rails 命令行</a></dd>
                <dd><a href="asset_pipeline.html">Asset Pipeline</a></dd>
                <dd><a href="working_with_javascript_in_rails.html">在 Rails 中使用 JavaScript</a></dd>
                <dd><a href="autoloading_and_reloading_constants.html">自动加载和重新加载常量</a></dd>
                <dd><a href="caching_with_rails.html">Rails 缓存概览</a></dd>
                <dd><a href="api_app.html">使用 Rails 开发只提供 API 的应用</a></dd>
                <dd><a href="action_cable_overview.html">Action Cable 概览</a></dd>
                <dt>扩展 Rails</dt>
                <dd><a href="rails_on_rack.html">Rails on Rack</a></dd>
                <dd><a href="generators.html">创建及定制 Rails 生成器和模板</a></dd>
                <dt>为 Ruby on Rails 做贡献</dt>
                <dd><a href="contributing_to_ruby_on_rails.html">为 Ruby on Rails 做贡献</a></dd>
                <dd><a href="api_documentation_guidelines.html">API 文档指导方针</a></dd>
                <dd><a href="ruby_on_rails_guides_guidelines.html">Ruby on Rails 指南指导方针</a></dd>
                <dt>维护方针</dt>
                <dd><a href="maintenance_policy.html">Ruby on Rails 的维护方针</a></dd>
                <dt>发布记</dt>
                <dd><a href="upgrading_ruby_on_rails.html">Ruby on Rails 升级指南</a></dd>
                <dd><a href="5_0_release_notes.html">Ruby on Rails 5.0 发布记</a></dd>
                <dd><a href="4_2_release_notes.html">Ruby on Rails 4.2 发布记</a></dd>
                <dd><a href="4_1_release_notes.html">Ruby on Rails 4.1 发布记</a></dd>
                <dd><a href="4_0_release_notes.html">Ruby on Rails 4.0 发布记</a></dd>
                <dd><a href="3_2_release_notes.html">Ruby on Rails 3.2 发布记</a></dd>
                <dd><a href="3_1_release_notes.html">Ruby on Rails 3.1 发布记</a></dd>
                <dd><a href="3_0_release_notes.html">Ruby on Rails 3.0 发布记</a></dd>
                <dd><a href="2_3_release_notes.html">Ruby on Rails 2.3 发布记</a></dd>
                <dd><a href="2_2_release_notes.html">Ruby on Rails 2.2 发布记</a></dd>
              </dl>
          </div>
        </li>
        <li><a class="nav-item" href="contributing_to_ruby_on_rails.html">贡献</a></li>
        <li><a class="nav-item" href="credits.html">感谢</a></li>
        <li class="guides-index guides-index-small">
          <select class="guides-index-item nav-item">
            <option value="index.html">指南索引</option>
              <optgroup label="新手入门">
                  <option value="getting_started.html">Rails 入门</option>
              </optgroup>
              <optgroup label="模型">
                  <option value="active_record_basics.html">Active Record 基础</option>
                  <option value="active_record_migrations.html">Active Record 迁移</option>
                  <option value="active_record_validations.html">Active Record 数据验证</option>
                  <option value="active_record_callbacks.html">Active Record 回调</option>
                  <option value="association_basics.html">Active Record 关联</option>
                  <option value="active_record_querying.html">Active Record 查询接口</option>
              </optgroup>
              <optgroup label="视图">
                  <option value="layouts_and_rendering.html">Rails 布局和视图渲染</option>
                  <option value="form_helpers.html">Action View 表单辅助方法</option>
              </optgroup>
              <optgroup label="控制器">
                  <option value="action_controller_overview.html">Action Controller 概览</option>
                  <option value="routing.html">Rails 路由全解</option>
              </optgroup>
              <optgroup label="深入探索">
                  <option value="active_support_core_extensions.html">Active Support 核心扩展</option>
                  <option value="i18n.html">Rails 国际化 API</option>
                  <option value="action_mailer_basics.html">Action Mailer 基础</option>
                  <option value="active_job_basics.html">Active Job 基础</option>
                  <option value="testing.html">Rails 应用测试指南</option>
                  <option value="security.html">Ruby on Rails 安全指南</option>
                  <option value="debugging_rails_applications.html">调试 Rails 应用</option>
                  <option value="configuring.html">配置 Rails 应用</option>
                  <option value="command_line.html">Rails 命令行</option>
                  <option value="asset_pipeline.html">Asset Pipeline</option>
                  <option value="working_with_javascript_in_rails.html">在 Rails 中使用 JavaScript</option>
                  <option value="autoloading_and_reloading_constants.html">自动加载和重新加载常量</option>
                  <option value="caching_with_rails.html">Rails 缓存概览</option>
                  <option value="api_app.html">使用 Rails 开发只提供 API 的应用</option>
                  <option value="action_cable_overview.html">Action Cable 概览</option>
              </optgroup>
              <optgroup label="扩展 Rails">
                  <option value="rails_on_rack.html">Rails on Rack</option>
                  <option value="generators.html">创建及定制 Rails 生成器和模板</option>
              </optgroup>
              <optgroup label="为 Ruby on Rails 做贡献">
                  <option value="contributing_to_ruby_on_rails.html">为 Ruby on Rails 做贡献</option>
                  <option value="api_documentation_guidelines.html">API 文档指导方针</option>
                  <option value="ruby_on_rails_guides_guidelines.html">Ruby on Rails 指南指导方针</option>
              </optgroup>
              <optgroup label="维护方针">
                  <option value="maintenance_policy.html">Ruby on Rails 的维护方针</option>
              </optgroup>
              <optgroup label="发布记">
                  <option value="upgrading_ruby_on_rails.html">Ruby on Rails 升级指南</option>
                  <option value="5_0_release_notes.html">Ruby on Rails 5.0 发布记</option>
                  <option value="4_2_release_notes.html">Ruby on Rails 4.2 发布记</option>
                  <option value="4_1_release_notes.html">Ruby on Rails 4.1 发布记</option>
                  <option value="4_0_release_notes.html">Ruby on Rails 4.0 发布记</option>
                  <option value="3_2_release_notes.html">Ruby on Rails 3.2 发布记</option>
                  <option value="3_1_release_notes.html">Ruby on Rails 3.1 发布记</option>
                  <option value="3_0_release_notes.html">Ruby on Rails 3.0 发布记</option>
                  <option value="2_3_release_notes.html">Ruby on Rails 2.3 发布记</option>
                  <option value="2_2_release_notes.html">Ruby on Rails 2.2 发布记</option>
              </optgroup>
          </select>
        </li>
      </ul>
    </div>
  </div>
  <hr class="hide" />

  <div id="feature">
    <div class="wrapper">
      <h2>创建及定制 Rails 生成器和模板</h2><p>如果你打算改进自己的工作流程，Rails 生成器是必备工具。本文教你创建及定制生成器的方式。</p><p>读完本文后，您将学到：</p>
<ul>
<li>  如何查看应用中有哪些生成器可用；</li>
<li>  如何使用模板创建生成器；</li>
<li>  在调用生成器之前，Rails 如何搜索生成器；</li>
<li>  Rails 内部如何使用模板生成 Rails 代码；</li>
<li>  如何通过创建新生成器定制脚手架；</li>
<li>  如何通过修改生成器模板定制脚手架；</li>
<li>  如何使用后备机制防范覆盖大量生成器；</li>
<li>  如何创建应用模板。</li>
</ul>


              <div id="subCol">
          <h3 class="chapter"><img src="images/chapters_icon.gif" alt="" />目录</h3>
          <ol class="chapters">
<li><a href="#first-contact">第一次接触</a></li>
<li><a href="#creating-your-first-generator">创建首个生成器</a></li>
<li><a href="#creating-generators-with-generators">使用生成器创建生成器</a></li>
<li><a href="#generators-lookup">查找生成器</a></li>
<li><a href="#customizing-your-workflow">定制工作流程</a></li>
<li><a href="#customizing-your-workflow-by-changing-generators-templates">通过修改生成器模板定制工作流程</a></li>
<li><a href="#adding-generators-fallbacks">为生成器添加后备机制</a></li>
<li><a href="#application-templates">应用模板</a></li>
<li><a href="#adding-command-line-arguments">添加命令行参数</a></li>
<li>
<a href="#generator-methods">生成器方法</a>

<ul>
<li><a href="#gem"><code>gem</code></a></li>
<li><a href="#gem-group"><code>gem_group</code></a></li>
<li><a href="#add-source"><code>add_source</code></a></li>
<li><a href="#inject-into-file"><code>inject_into_file</code></a></li>
<li><a href="#gsub-file"><code>gsub_file</code></a></li>
<li><a href="#application"><code>application</code></a></li>
<li><a href="#git"><code>git</code></a></li>
<li><a href="#vendor"><code>vendor</code></a></li>
<li><a href="#lib"><code>lib</code></a></li>
<li><a href="#rakefile"><code>rakefile</code></a></li>
<li><a href="#initializer"><code>initializer</code></a></li>
<li><a href="#generate"><code>generate</code></a></li>
<li><a href="#rake"><code>rake</code></a></li>
<li><a href="#capify-bang"><code>capify!</code></a></li>
<li><a href="#route"><code>route</code></a></li>
<li><a href="#readme"><code>readme</code></a></li>
</ul>
</li>
</ol>

        </div>

    </div>
  </div>

  <div id="container">
    <div class="wrapper">
      <div id="mainCol">
        <p><a class="anchor" id="first-contact"></a></p><h3 id="first-contact">1 第一次接触</h3><p>使用 <code>rails</code> 命令创建应用时，使用的其实就是一个 Rails 生成器。创建应用之后，可以使用 <code>rails generator</code> 命令列出全部可用的生成器：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails new myapp
$ cd myapp
$ bin/rails generate

</pre>
</div>
<p>你会看到 Rails 自带的全部生成器。如果想查看生成器的详细描述，比如说 <code>helper</code> 生成器，可以这么做：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails generate helper --help

</pre>
</div>
<p><a class="anchor" id="creating-your-first-generator"></a></p><h3 id="creating-your-first-generator">2 创建首个生成器</h3><p>自 Rails 3.0 起，生成器使用 <a href="https://github.com/erikhuda/thor">Thor</a> 构建。Thor 提供了强大的解析选项和处理文件的丰富 API。举个例子。我们来构建一个生成器，在 <code>config/initializers</code> 目录中创建一个名为 <code>initializer.rb</code> 的初始化脚本。</p><p>第一步是创建 <code>lib/generators/initializer_generator.rb</code> 文件，写入下述内容：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class InitializerGenerator &lt; Rails::Generators::Base
  def create_initializer_file
    create_file "config/initializers/initializer.rb", "# 这里是初始化文件的内容"
  end
end

</pre>
</div>
<div class="note"><p><code>create_file</code> 是 <code>Thor::Actions</code> 提供的一个方法。<code>create_file</code> 即其他 Thor 方法的文档参见 <a href="http://rdoc.info/github/erikhuda/thor/master/Thor/Actions.html">Thor 的文档</a>。</p></div><p>这个生成器相当简单：继承自 <code>Rails::Generators::Base</code>，定义了一个方法。调用生成器时，生成器中的公开方法按照定义的顺序依次执行。最后，我们调用 <code>create_file</code> 方法在指定的位置创建一个文件，写入指定的内容。如果你熟悉 Rails Application Templates API，对这个生成器 API 就不会感到陌生。</p><p>若想调用这个生成器，只需这么做：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails generate initializer

</pre>
</div>
<p>在继续之前，先看一下这个生成器的描述：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails generate initializer --help

</pre>
</div>
<p>如果把生成器放在命名空间里（如 <code>ActiveRecord::Generators::ModelGenerator</code>），Rails 通常能生成好的描述，但这里没有。这一问题有两个解决方法。第一个是，在生成器中调用 <code>desc</code>：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class InitializerGenerator &lt; Rails::Generators::Base
  desc "This generator creates an initializer file at config/initializers"
  def create_initializer_file
    create_file "config/initializers/initializer.rb", "# Add initialization content here"
  end
end

</pre>
</div>
<p>现在，调用生成器时指定 <code>--help</code> 选项便能看到刚添加的描述。添加描述的第二个方法是，在生成器所在的目录中创建一个名为 <code>USAGE</code> 的文件。下一节将这么做。</p><p><a class="anchor" id="creating-generators-with-generators"></a></p><h3 id="creating-generators-with-generators">3 使用生成器创建生成器</h3><p>生成器本身也有一个生成器：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails generate generator initializer
      create  lib/generators/initializer
      create  lib/generators/initializer/initializer_generator.rb
      create  lib/generators/initializer/USAGE
      create  lib/generators/initializer/templates

</pre>
</div>
<p>下述代码是这个生成器生成的：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class InitializerGenerator &lt; Rails::Generators::NamedBase
  source_root File.expand_path("../templates", __FILE__)
end

</pre>
</div>
<p>首先注意，我们继承的是 <code>Rails::Generators::NamedBase</code>，而不是 <code>Rails::Generators::Base</code>。这表明，我们的生成器至少需要一个参数，即初始化脚本的名称，在代码中通过 <code>name</code> 变量获取。</p><p>查看这个生成器的描述可以证实这一点（别忘了删除旧的生成器文件）：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails generate initializer --help
Usage:
  rails generate initializer NAME [options]

</pre>
</div>
<p>还能看到，这个生成器有个名为 <code>source_root</code> 的类方法。这个方法指向生成器模板（如果有的话）所在的位置，默认是生成的 <code>lib/generators/initializer/templates</code> 目录。</p><p>为了弄清生成器模板的作用，下面创建 <code>lib/generators/initializer/templates/initializer.rb</code> 文件，写入下述内容：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
# Add initialization content here

</pre>
</div>
<p>然后修改生成器，调用时复制这个模板：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class InitializerGenerator &lt; Rails::Generators::NamedBase
  source_root File.expand_path("../templates", __FILE__)

  def copy_initializer_file
    copy_file "initializer.rb", "config/initializers/#{file_name}.rb"
  end
end

</pre>
</div>
<p>下面执行这个生成器：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails generate initializer core_extensions

</pre>
</div>
<p>可以看到，这个命令生成了 <code>config/initializers/core_extensions.rb</code> 文件，里面的内容与模板中一样。这表明，<code>copy_file</code> 方法的作用是把源根目录中的文件复制到指定的目标路径。<code>file_name</code> 方法是继承自 <code>Rails::Generators::NamedBase</code> 之后自动创建的。</p><p>生成器中可用的方法在本章<a href="#generator-methods">最后一节</a>说明。</p><p><a class="anchor" id="generators-lookup"></a></p><h3 id="generators-lookup">4 查找生成器</h3><p>执行 <code>rails generate initializer core_extensions</code> 命令时，Rails 按照下述顺序引入文件，直到找到所需的生成器为止：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
rails/generators/initializer/initializer_generator.rb
generators/initializer/initializer_generator.rb
rails/generators/initializer_generator.rb
generators/initializer_generator.rb

</pre>
</div>
<p>如果最后找不到，显示一个错误消息。</p><div class="info"><p>上述示例把文件放在应用的 <code>lib</code> 目录中，因为这个目录在 <code>$LOAD_PATH</code> 中。</p></div><p><a class="anchor" id="customizing-your-workflow"></a></p><h3 id="customizing-your-workflow">5 定制工作流程</h3><p>Rails 自带的生成器十分灵活，可以定制脚手架。生成器在 <code>config/application.rb</code> 文件中配置，下面是一些默认值：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
config.generators do |g|
  g.orm             :active_record
  g.template_engine :erb
  g.test_framework  :test_unit, fixture: true
end

</pre>
</div>
<p>在定制工作流程之前，先看看脚手架是什么：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails generate scaffold User name:string
      invoke  active_record
      create    db/migrate/20130924151154_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
      invoke  resource_route
       route    resources :users
      invoke  scaffold_controller
      create    app/controllers/users_controller.rb
      invoke    erb
      create      app/views/users
      create      app/views/users/index.html.erb
      create      app/views/users/edit.html.erb
      create      app/views/users/show.html.erb
      create      app/views/users/new.html.erb
      create      app/views/users/_form.html.erb
      invoke    test_unit
      create      test/controllers/users_controller_test.rb
      invoke    helper
      create      app/helpers/users_helper.rb
      invoke    jbuilder
      create      app/views/users/index.json.jbuilder
      create      app/views/users/show.json.jbuilder
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/users.coffee
      invoke    scss
      create      app/assets/stylesheets/users.scss
      invoke  scss
      create    app/assets/stylesheets/scaffolds.scss

</pre>
</div>
<p>通过上述输出不难看出 Rails 3.0 及以上版本中生成器的工作方式。脚手架生成器其实什么也不生成，只是调用其他生成器。因此，我们可以添加、替换和删除任何生成器。例如，脚手架生成器调用了 scaffold_controller 生成器，而它调用了 erb、test_unit 和 helper 生成器。因为各个生成器的职责单一，所以可以轻易复用，从而避免代码重复。</p><p>使用脚手架生成资源时，如果不想生成默认的 <code>app/assets/stylesheets/scaffolds.scss</code> 文件，可以禁用 <code>scaffold_stylesheet</code>：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
  config.generators do |g|
    g.scaffold_stylesheet false
  end

</pre>
</div>
<p>其次，我们可以不让脚手架生成样式表、JavaScript 和测试固件文件。为此，我们要像下面这样修改配置：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
config.generators do |g|
  g.orm             :active_record
  g.template_engine :erb
  g.test_framework  :test_unit, fixture: false
  g.stylesheets     false
  g.javascripts     false
end

</pre>
</div>
<p>如果再使用脚手架生成器生成一个资源，你会看到，它不再创建样式表、JavaScript 和固件文件了。如果想进一步定制，例如使用 DataMapper 和 RSpec 替换 Active Record 和 TestUnit，只需添加相应的 gem，然后配置生成器。</p><p>下面举个例子。我们将创建一个辅助方法生成器，添加一些实例变量读值方法。首先，在 rails 命名空间（Rails 在这里搜索作为钩子的生成器）中创建一个生成器：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails generate generator rails/my_helper
      create  lib/generators/rails/my_helper
      create  lib/generators/rails/my_helper/my_helper_generator.rb
      create  lib/generators/rails/my_helper/USAGE
      create  lib/generators/rails/my_helper/templates

</pre>
</div>
<p>然后，把 <code>templates</code> 目录和 <code>source_root</code> 类方法删除，因为用不到。然后添加下述方法，此时生成器如下所示：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
# lib/generators/rails/my_helper/my_helper_generator.rb
class Rails::MyHelperGenerator &lt; Rails::Generators::NamedBase
  def create_helper_file
    create_file "app/helpers/#{file_name}_helper.rb", &lt;&lt;-FILE
module #{class_name}Helper
  attr_reader :#{plural_name}, :#{plural_name.singularize}
end
    FILE
  end
end

</pre>
</div>
<p>下面为 products 创建一个辅助方法，试试这个新生成器：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails generate my_helper products
      create  app/helpers/products_helper.rb

</pre>
</div>
<p>上述命令会在 <code>app/helpers</code> 目录中生成下述辅助方法文件：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
module ProductsHelper
  attr_reader :products, :product
end

</pre>
</div>
<p>这正是我们预期的。接下来再次编辑 <code>config/application.rb</code>，告诉脚手架使用这个新辅助方法生成器：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
config.generators do |g|
  g.orm             :active_record
  g.template_engine :erb
  g.test_framework  :test_unit, fixture: false
  g.stylesheets     false
  g.javascripts     false
  g.helper          :my_helper
end

</pre>
</div>
<p>然后调用这个生成器，实测一下：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails generate scaffold Article body:text
      [...]
      invoke    my_helper
      create      app/helpers/articles_helper.rb

</pre>
</div>
<p>从输出中可以看出，Rails 调用了这个新辅助方法生成器，而不是默认的那个。不过，少了点什么：没有生成测试。我们将复用旧的辅助方法生成器测试。</p><p>自 Rails 3.0 起，测试很容易，因为有了钩子。辅助方法无需限定于特定的测试框架，只需提供一个钩子，让测试框架实现钩子即可。</p><p>为此，我们可以按照下述方式修改生成器：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
# lib/generators/rails/my_helper/my_helper_generator.rb
class Rails::MyHelperGenerator &lt; Rails::Generators::NamedBase
  def create_helper_file
    create_file "app/helpers/#{file_name}_helper.rb", &lt;&lt;-FILE
module #{class_name}Helper
  attr_reader :#{plural_name}, :#{plural_name.singularize}
end
    FILE
  end

  hook_for :test_framework
end

</pre>
</div>
<p>现在，如果再调用这个辅助方法生成器，而且配置的测试框架是 TestUnit，它会调用 <code>Rails::TestUnitGenerator</code> 和 <code>TestUnit::MyHelperGenerator</code>。这两个生成器都没定义，我们可以告诉生成器去调用 <code>TestUnit::Generators::HelperGenerator</code>。这个生成器是 Rails 自带的。为此，我们只需添加：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
# 搜索 :helper，而不是 :my_helper
hook_for :test_framework, as: :helper

</pre>
</div>
<p>现在，你可以使用脚手架再生成一个资源，你会发现它生成了测试。</p><p><a class="anchor" id="customizing-your-workflow-by-changing-generators-templates"></a></p><h3 id="customizing-your-workflow-by-changing-generators-templates">6 通过修改生成器模板定制工作流程</h3><p>前面我们只想在生成的辅助方法中添加一行代码，而不增加额外的功能。为此有种更为简单的方式：替换现有生成器的模板。这里要替换的是 <code>Rails::Generators::HelperGenerator</code> 的模板。</p><p>在 Rails 3.0 及以上版本中，生成器搜索模板时不仅查看源根目录，还会在其他路径中搜索模板。其中一个是 <code>lib/templates</code>。我们要定制的是 <code>Rails::Generators::HelperGenerator</code>，因此可以在 <code>lib/templates/rails/helper</code> 目录中放一个模板副本，名为 <code>helper.rb</code>。创建这个文件，写入下述内容：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
module &lt;%= class_name %&gt;Helper
  attr_reader :&lt;%= plural_name %&gt;, :&lt;%= plural_name.singularize %&gt;
end

</pre>
</div>
<p>然后撤销之前对 <code>config/application.rb</code> 文件的修改：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
config.generators do |g|
  g.orm             :active_record
  g.template_engine :erb
  g.test_framework  :test_unit, fixture: false
  g.stylesheets     false
  g.javascripts     false
end

</pre>
</div>
<p>再生成一个资源，你将看到，得到的结果完全一样。如果你想定制脚手架模板和（或）布局，只需在 <code>lib/templates/erb/scaffold</code> 目录中创建 <code>edit.html.erb</code>、<code>index.html.erb</code>，等等。</p><p>Rails 的脚手架模板经常使用 ERB 标签，这些标签要转义，这样生成的才是有效的 ERB 代码。</p><p>例如，在模板中要像下面这样转义 ERB 标签（注意多了个 %）：</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
&lt;%%= stylesheet_include_tag :application %&gt;

</pre>
</div>
<p>生成的内容如下：</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
&lt;%= stylesheet_include_tag :application %&gt;

</pre>
</div>
<p><a class="anchor" id="adding-generators-fallbacks"></a></p><h3 id="adding-generators-fallbacks">7 为生成器添加后备机制</h3><p>生成器最后一个相当有用的功能是插件生成器的后备机制。比如说我们想在 TestUnit 的基础上添加类似 <a href="https://github.com/thoughtbot/shoulda">shoulda</a> 的功能。因为 TestUnit 已经实现了 Rails 所需的全部生成器，而 shoulda 只是覆盖其中部分，所以 shoulda 没必要重新实现某些生成器。相反，shoulda 可以告诉 Rails，在 <code>Shoulda</code> 命名空间中找不到某个生成器时，使用 <code>TestUnit</code> 中的生成器。</p><p>我们可以再次修改 <code>config/application.rb</code> 文件，模拟这种行为：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
config.generators do |g|
  g.orm             :active_record
  g.template_engine :erb
  g.test_framework  :shoulda, fixture: false
  g.stylesheets     false
  g.javascripts     false

  # 添加后备机制
  g.fallbacks[:shoulda] = :test_unit
end

</pre>
</div>
<p>现在，使用脚手架生成 Comment 资源时，你会看到调用了 shoulda 生成器，而它调用的其实是 TestUnit 生成器：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails generate scaffold Comment body:text
      invoke  active_record
      create    db/migrate/20130924143118_create_comments.rb
      create    app/models/comment.rb
      invoke    shoulda
      create      test/models/comment_test.rb
      create      test/fixtures/comments.yml
      invoke  resource_route
       route    resources :comments
      invoke  scaffold_controller
      create    app/controllers/comments_controller.rb
      invoke    erb
      create      app/views/comments
      create      app/views/comments/index.html.erb
      create      app/views/comments/edit.html.erb
      create      app/views/comments/show.html.erb
      create      app/views/comments/new.html.erb
      create      app/views/comments/_form.html.erb
      invoke    shoulda
      create      test/controllers/comments_controller_test.rb
      invoke    my_helper
      create      app/helpers/comments_helper.rb
      invoke    jbuilder
      create      app/views/comments/index.json.jbuilder
      create      app/views/comments/show.json.jbuilder
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/comments.coffee
      invoke    scss

</pre>
</div>
<p>后备机制能让生成器专注于实现单一职责，尽量复用代码，减少重复代码量。</p><p><a class="anchor" id="application-templates"></a></p><h3 id="application-templates">8 应用模板</h3><p>至此，我们知道生成器可以在应用内部使用，但是你知道吗，生成器也可用于生成应用？这种生成器叫“模板”（template）。本节简介 Templates API，详情参阅<a href="rails_application_templates.html">Rails 应用模板</a>。</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
gem "rspec-rails", group: "test"
gem "cucumber-rails", group: "test"

if yes?("Would you like to install Devise?")
  gem "devise"
  generate "devise:install"
  model_name = ask("What would you like the user model to be called? [user]")
  model_name = "user" if model_name.blank?
  generate "devise", model_name
end

</pre>
</div>
<p>在上述模板中，我们指定应用要使用 <code>rspec-rails</code> 和 <code>cucumber-rails</code> 两个 gem，因此把它们添加到 <code>Gemfile</code> 的 <code>test</code> 组。然后，我们询问用户是否想安装 Devise。如果用户回答“y”或“yes”，这个模板会将其添加到 <code>Gemfile</code> 中，而且不放在任何分组中，然后运行 <code>devise:install</code> 生成器。然后，这个模板获取用户的输入，运行 <code>devise</code> 生成器，并传入用户对前一个问题的回答。</p><p>假如这个模板保存在名为 <code>template.rb</code> 的文件中。我们可以使用它修改 <code>rails new</code> 命令的输出，方法是把文件名传给 <code>-m</code> 选项：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails new thud -m template.rb

</pre>
</div>
<p>上述命令会生成 Thud 应用，然后把模板应用到生成的输出上。</p><p>模板不一定非得存储在本地系统中，<code>-m</code> 选项也支持在线模板：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails new thud -m https://gist.github.com/radar/722911/raw/

</pre>
</div>
<p>本章最后一节虽然不说明如何生成大多数已知的优秀模板，但是会详细说明可用的方法，供你自己开发模板。那些方法也可以在生成器中使用。</p><p><a class="anchor" id="adding-command-line-arguments"></a></p><h3 id="adding-command-line-arguments">9 添加命令行参数</h3><p>Rails 的生成器可以轻易修改，接受自定义的命令行参数。这个功能源自 <a href="http://www.rubydoc.info/github/erikhuda/thor/master/Thor/Base/ClassMethods#class_option-instance_method">Thor</a>：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class_option :scope, type: :string, default: 'read_products'

</pre>
</div>
<p>现在，生成器可以这样调用：</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails generate initializer --scope write_products

</pre>
</div>
<p>在生成器类内部，命令行参数通过 <code>options</code> 方法访问。</p><p><a class="anchor" id="generator-methods"></a></p><h3 id="generator-methods">10 生成器方法</h3><p>下面是可供 Rails 生成器和模板使用的方法。</p><div class="note"><p>本文不涵盖 Thor 提供的方法。如果想了解，参阅 <a href="http://rdoc.info/github/erikhuda/thor/master/Thor/Actions.html">Thor 的文档</a>。</p></div><p><a class="anchor" id="gem"></a></p><h4 id="gem">10.1 <code>gem</code>
</h4><p>指定应用的一个 gem 依赖。</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
gem "rspec", group: "test", version: "2.1.0"
gem "devise", "1.1.5"

</pre>
</div>
<p>可用的选项：</p>
<ul>
<li>  <code>:group</code>：把 gem 添加到 <code>Gemfile</code> 中的哪个分组里。</li>
<li>  <code>:version</code>：要使用的 gem 版本号，字符串。也可以在 <code>gem</code> 方法的第二个参数中指定。</li>
<li>  <code>:git</code>：gem 的 Git 仓库的 URL。</li>
</ul>
<p>传给这个方法的其他选项放在行尾：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
gem "devise", git: "git://github.com/plataformatec/devise", branch: "master"

</pre>
</div>
<p>上述代码在 <code>Gemfile</code> 中写入下面这行代码：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
gem "devise", git: "git://github.com/plataformatec/devise", branch: "master"

</pre>
</div>
<p><a class="anchor" id="gem-group"></a></p><h4 id="gem-group">10.2 <code>gem_group</code>
</h4><p>把 gem 放在一个分组里：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
gem_group :development, :test do
  gem "rspec-rails"
end

</pre>
</div>
<p><a class="anchor" id="add-source"></a></p><h4 id="add-source">10.3 <code>add_source</code>
</h4><p>在 <code>Gemfile</code> 中添加指定的源：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
add_source "http://gems.github.com"

</pre>
</div>
<p>这个方法也接受块：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
add_source "http://gems.github.com" do
  gem "rspec-rails"
end

</pre>
</div>
<p><a class="anchor" id="inject-into-file"></a></p><h4 id="inject-into-file">10.4 <code>inject_into_file</code>
</h4><p>在文件中的指定位置插入一段代码：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
inject_into_file 'name_of_file.rb', after: "#The code goes below this line. Don't forget the Line break at the end\n" do &lt;&lt;-'RUBY'
  puts "Hello World"
RUBY
end

</pre>
</div>
<p><a class="anchor" id="gsub-file"></a></p><h4 id="gsub-file">10.5 <code>gsub_file</code>
</h4><p>替换文件中的文本：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
gsub_file 'name_of_file.rb', 'method.to_be_replaced', 'method.the_replacing_code'

</pre>
</div>
<p>使用正则表达式替换的效果更精准。可以使用类似的方式调用 <code>append_file</code> 和 <code>prepend_file</code>，分别在文件的末尾和开头添加代码。</p><p><a class="anchor" id="application"></a></p><h4 id="application">10.6 <code>application</code>
</h4><p>在 <code>config/application.rb</code> 文件中应用类定义后面直接添加内容：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
application "config.asset_host = 'http://example.com'"

</pre>
</div>
<p>这个方法也接受块：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
application do
  "config.asset_host = 'http://example.com'"
end

</pre>
</div>
<p>可用的选项：</p>
<ul>
<li>
<p>  <code>:env</code>：指定配置选项所属的环境。如果想在块中使用这个选项，建议使用下述句法：</p>
<div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
application(nil, env: "development") do
  "config.asset_host = 'http://localhost:3000'"
end

</pre>
</div>
</li>
</ul>
<p><a class="anchor" id="git"></a></p><h4 id="git">10.7 <code>git</code>
</h4><p>运行指定的 Git 命令：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
git :init
git add: "."
git commit: "-m First commit!"
git add: "onefile.rb", rm: "badfile.cxx"

</pre>
</div>
<p>这里的散列是传给指定 Git 命令的参数或选项。如最后一行所示，一次可以指定多个 Git 命令，但是命令的运行顺序不一定与指定的顺序一样。</p><p><a class="anchor" id="vendor"></a></p><h4 id="vendor">10.8 <code>vendor</code>
</h4><p>在 <code>vendor</code> 目录中放一个文件，内有指定的代码：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
vendor "sekrit.rb", '#top secret stuff'

</pre>
</div>
<p>这个方法也接受块：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
vendor "seeds.rb" do
  "puts 'in your app, seeding your database'"
end

</pre>
</div>
<p><a class="anchor" id="lib"></a></p><h4 id="lib">10.9 <code>lib</code>
</h4><p>在 <code>lib</code> 目录中放一个文件，内有指定的代码：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
lib "special.rb", "p Rails.root"

</pre>
</div>
<p>这个方法也接受块</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
lib "super_special.rb" do
  puts "Super special!"
end

</pre>
</div>
<p><a class="anchor" id="rakefile"></a></p><h4 id="rakefile">10.10 <code>rakefile</code>
</h4><p>在应用的 <code>lib/tasks</code> 目录中创建一个 Rake 文件：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
rakefile "test.rake", "hello there"

</pre>
</div>
<p>这个方法也接受块：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
rakefile "test.rake" do
  %Q{
    task rock: :environment do
      puts "Rockin'"
    end
  }
end

</pre>
</div>
<p><a class="anchor" id="initializer"></a></p><h4 id="initializer">10.11 <code>initializer</code>
</h4><p>在应用的 <code>config/initializers</code> 目录中创建一个初始化脚本：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
initializer "begin.rb", "puts 'this is the beginning'"

</pre>
</div>
<p>这个方法也接受块，期待返回一个字符串：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
initializer "begin.rb" do
  "puts 'this is the beginning'"
end

</pre>
</div>
<p><a class="anchor" id="generate"></a></p><h4 id="generate">10.12 <code>generate</code>
</h4><p>运行指定的生成器，第一个参数是生成器的名称，后续参数直接传给生成器：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
generate "scaffold", "forums title:string description:text"

</pre>
</div>
<p><a class="anchor" id="rake"></a></p><h4 id="rake">10.13 <code>rake</code>
</h4><p>运行指定的 Rake 任务：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
rake "db:migrate"

</pre>
</div>
<p>可用的选项：</p>
<ul>
<li>  <code>:env</code>：指定在哪个环境中运行 Rake 任务。</li>
<li>  <code>:sudo</code>：是否使用 <code>sudo</code> 运行任务。默认为 <code>false</code>。</li>
</ul>
<p><a class="anchor" id="capify-bang"></a></p><h4 id="capify-bang">10.14 <code>capify!</code>
</h4><p>在应用的根目录中运行 Capistrano 提供的 <code>capify</code> 命令，生成 Capistrano 配置。</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
capify!

</pre>
</div>
<p><a class="anchor" id="route"></a></p><h4 id="route">10.15 <code>route</code>
</h4><p>在 <code>config/routes.rb</code> 文件中添加文本：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
route "resources :people"

</pre>
</div>
<p><a class="anchor" id="readme"></a></p><h4 id="readme">10.16 <code>readme</code>
</h4><p>输出模板的 <code>source_path</code> 中某个文件的内容，通常是 README 文件：</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
readme "README"

</pre>
</div>


        <h3>反馈</h3>
        <p>
          我们鼓励您帮助提高本指南的质量。
        </p>
        <p>
          如果看到如何错字或错误，请反馈给我们。
          您可以阅读我们的<a href="http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation">文档贡献</a>指南。
        </p>
        <p>
          您还可能会发现内容不完整或不是最新版本。
          请添加缺失文档到 master 分支。请先确认 <a href="http://edgeguides.rubyonrails.org">Edge Guides</a> 是否已经修复。
          关于用语约定，请查看<a href="ruby_on_rails_guides_guidelines.html">Ruby on Rails 指南指导</a>。
        </p>
        <p>
          无论什么原因，如果你发现了问题但无法修补它，请<a href="https://github.com/rails/rails/issues">创建 issue</a>。
        </p>
        <p>
          最后，欢迎到 <a href="http://groups.google.com/group/rubyonrails-docs">rubyonrails-docs 邮件列表</a>参与任何有关 Ruby on Rails 文档的讨论。
        </p>
        <h4>中文翻译反馈</h4>
        <p>贡献：<a href="https://github.com/ruby-china/guides">https://github.com/ruby-china/guides</a>。</p>
      </div>
    </div>
  </div>

  <hr class="hide" />
  <div id="footer">
    <div class="wrapper">
      <p>本著作采用 <a href="https://creativecommons.org/licenses/by-sa/4.0/">创作共用 署名-相同方式共享 4.0 国际</a> 授权</p>
<p>“Rails”，“Ruby on Rails”，以及 Rails Logo 为 David Heinemeier Hansson 的商标。版权所有</p>

    </div>
  </div>

  <script type="text/javascript" src="javascripts/jquery.min.js"></script>
  <script type="text/javascript" src="javascripts/responsive-tables.js"></script>
  <script type="text/javascript" src="javascripts/guides.js"></script>
  <script type="text/javascript" src="javascripts/syntaxhighlighter.js"></script>
  <script type="text/javascript">
    syntaxhighlighterConfig = {
      autoLinks: false,
    };
    $(guidesIndex.bind);
  </script>
</body>
</html>
